
;;;
;;; Monoalphabetic encipher and decipher
;;;
;;; Copyright 2016 Jason K. MacDuffie
;;; License: GPLv3+
;;;

(define (key-to-full-key key-in)
  ;; First, add full alphabet to the end of key-in
  (define combined-key (append key-in (string->list "ABCDEFGHIJKLMNOPQRSTUVWXYZ")))
  ;; Then, remove duplicates from that result
  (let loop ((result '())
             (next combined-key))
    (if (null? next)
        (reverse result)
        (loop (if (member (car next) result)
                  result
                  (cons (car next) result))
              (cdr next)))))

(define (mono-encipher pt-in key-in)
  (inner-mono-encipher pt-in (key-to-full-key key-in)))

(define (mono-decipher pt-in key-in)
  (inner-mono-decipher pt-in (key-to-full-key key-in)))

(define (inner-mono-encipher pt-in full-key-in)
  ;; Encipher pt-in with a monoalphabetic cipher, where
  ;; key-in is a list of the letters with which to replace
  ;; the plaintext letters.
  (define vector-key (list->vector full-key-in))
  (map (lambda (c) (vector-ref vector-key
                               (letter->integer c)))
       pt-in))

(define (inner-mono-decipher ct-in full-key-in)
  ;; Inverse of mono-encipher
  (define vector-key (make-vector 26))
  (let loop ((i 0)
             (in full-key-in))
    ;; We need to build the inverse key
    (when (< i 26)
      (vector-set! vector-key
                   (letter->integer (car in))
                   (integer->letter i))
      (loop (+ i 1) (cdr in))))
  (map (lambda (c) (vector-ref vector-key
                               (letter->integer c)))
       ct-in))

